package com.example.autumn.web;

import com.example.autumn.context.AnnotationConfigApplicationContext;
import com.example.autumn.context.ApplicationContext;
import com.example.autumn.exeception.NestedRuntimeException;
import com.example.autumn.io.PropertyResolver;
import com.example.autumn.web.utils.WebUtils;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author liuzhiyong
 * @date 2023/11/8
 * description: 监听Tomcat的启动, 加载yml或者properties配置文件, 初始化DispatcherServlet, 初始化IOC容器
 */
public class ContextLoaderListener implements ServletContextListener {

    final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 监听容器启动, 容器启动时调用
     * @param sce
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        logger.info("init {}.", getClass().getName());
        // 获取Servlet上下文
        ServletContext servletContext = sce.getServletContext();
        WebMvcConfiguration.setServletContext(servletContext);

        // 获取配置文件属性解析器
        PropertyResolver propertyResolver = WebUtils.createPropertyResolver();
        // 获取配置文件字符编码, 未配置的话默认是UTF-8
        String encoding = propertyResolver.getProperty("${autumn.web.character-encoding:UTF-8}");
        // 设置字符编码
        servletContext.setRequestCharacterEncoding(encoding);
        servletContext.setResponseCharacterEncoding(encoding);
        // 从web-inf中的web.xml文件中获取配置的启动类, 创建IOC容器
        ApplicationContext applicationContext = createApplicationContext(servletContext.getInitParameter("configuration"), propertyResolver);
        // 注册过滤器
        WebUtils.registerFilters(servletContext);
        // 注册 DispatcherServlet
        WebUtils.registerDispatcherServlet(servletContext, propertyResolver);
        // 添加IOC容器到Servlet
        servletContext.setAttribute("applicationContext", applicationContext);
    }

    /**
     * 创建IOC容器, 执行IOC容器初始化过程
     *
     * @param configClassName 配置类的全限定类名, 类似于SpringBoot的启动类的全限定类名
     * @param propertyResolver 属性解析器
     * @return {@link ApplicationContext } IOC容器
     * @author liuzhiyong
     * @date 2023/11/8
     */
    ApplicationContext createApplicationContext(String configClassName,
                                                PropertyResolver propertyResolver) {
        logger.info("init ApplicationContext by configuration: {}", configClassName);
        if (configClassName == null || configClassName.isEmpty()) {
            throw new NestedRuntimeException("Cannot init ApplicationContext for missing init param name: configuration");
        }
        Class<?> configClass;
        try {
            configClass = Class.forName(configClassName);
        } catch (ClassNotFoundException e) {
            throw new NestedRuntimeException("Could not load class from init param 'configuration': " + configClassName);
        }
        return new AnnotationConfigApplicationContext(configClass, propertyResolver);
    }


}
